package com.ego.order.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ego.common.constant.*;
import com.ego.common.exception.GlobalException;
import com.ego.common.to.order.OrderTo;
import com.ego.common.to.order.SeckillOrderTo;
import com.ego.common.util.PageUtil;
import com.ego.common.util.Result;
import com.ego.common.vo.BaseSearchVo;
import com.ego.common.vo.PageVo;
import com.ego.common.vo.QuarterlyDataVo;
import com.ego.common.vo.WeeklyDataVo;
import com.ego.common.vo.member.MemberReceiveAddressVo;
import com.ego.common.vo.order.OrderSubmitVo;
import com.ego.common.vo.product.CartVo;
import com.ego.order.feign.MemberFeign;
import com.ego.order.feign.ProductFeign;
import com.ego.order.feign.WareFeign;
import com.ego.order.po.OrderItem;
import com.ego.order.po.PaymentInfo;
import com.ego.order.service.OrderItemService;
import com.ego.order.service.PaymentInfoService;
import com.ego.common.to.order.OrderDetailTo;
import com.ego.common.to.order.OrderItemTo;
import com.ego.order.to.ware.WareLockTo;
import com.ego.order.vo.OrderConfirmVo;
import com.ego.common.vo.order.PayAsyncVo;
import com.ego.order.vo.PayVo;
import com.ego.order.vo.WebParamVo;
import com.ego.order.vo.product.SkuInfoVo;
import com.ego.order.vo.product.SpuInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.ego.order.dao.OrderDao;
import com.ego.order.po.Order;
import com.ego.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author KamTang
 * @program: ego
 * @description 订单对应实现类
 * @date 2022-11-13 11:25:07
 */
@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, Order> implements OrderService {

    private ThreadLocal<OrderSubmitVo> submitThreadLocal = new ThreadLocal<>();

    @Autowired
    private MemberFeign memberFeign;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ProductFeign productFeign;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private WareFeign wareFeign;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private PaymentInfoService paymentInfoService;
    @Autowired
    private ThreadPoolExecutor executor;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderDetailTo submit(OrderSubmitVo submitVo) {
        submitThreadLocal.set(submitVo);
        // 验证令牌【令牌的对比和删除必须保证原子性】
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String orderToken = submitVo.getOrderToken();
        // 1:success,0:fail
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(OrderConstant.ORDER_TOKEN_PREFIX + submitVo.getMemberId()), orderToken);
        // 令牌通过
        if (result != null && result == 1L) {
            // 创建订单
            OrderDetailTo orderTo = create();
            // 验价
            BigDecimal payAmount = orderTo.getOrder().getPayAmount();
            BigDecimal payPrice = submitVo.getPayPrice();
            // 如果两个价格的相差范围小于0.01就算相同
            if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < OrderConstant.ORDER_IGNORE_PRICE_DIFFERENCE) {
                // 入库
                saveTo(orderTo);

                // 库存锁定，只要有异常就回滚
                WareLockTo wareLock = new WareLockTo();
                wareLock.setOrderSn(orderTo.getOrder().getOrderSn());
                List<OrderItemTo> needLockedOrderItems = orderTo.getOrderItems().stream().map(item -> {
                    OrderItemTo orderItemTo = new OrderItemTo();
                    orderItemTo.setSkuId(item.getSkuId());
                    orderItemTo.setSkuQuantity(item.getSkuQuantity());
                    orderItemTo.setSkuName(item.getSkuName());
                    return orderItemTo;
                }).collect(Collectors.toList());
                wareLock.setLocks(needLockedOrderItems);

                Result lockResult = wareFeign.lock(wareLock);
                // 锁成功了
                if (lockResult.getCode() == SystemConstant.Status.SUCCESS.getCode()) {
                    // 订单创建成功，向MQ发送消息
                    rabbitTemplate.convertAndSend("order-event-exchange", "order.create", orderTo.getOrder());
                    log.info("订单创建成功，向MQ发送消息：{}", orderTo.getOrder());
                    return orderTo;
                } else {
                    throw new GlobalException(lockResult.getMsg(), lockResult.getCode());
                }

            }
        }
        return null;
    }

    @Override
    public Order get(String orderSn) {
        return this.getOne(new QueryWrapper<Order>().eq("order_sn", orderSn));
    }

    @Override
    public void close(OrderTo orderTo) {
        // 当前订单的最新状态
        Order order = this.getById(orderTo);
        if (order.closeable()) {
            Order po = new Order();
            po.close(order.getId());
            this.updateById(po);

            // TODO 保证消息一定会发出去，每一个消息都可以做好日志记录（给数据库保存每一个消息的详细信息）。
            try {
                // 关闭成功，也要把关单成功的消息发送给库存服务
                rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", order.toTo());
                log.info("订单创建成功，向MQ发送消息：{}", order.toTo());
            } catch (Exception e) {
                // TODO 将失败的消息进行重发
            }

        }
    }

    @Override
    public PayVo getPayInfo(String orderSn) {
        PayVo payVo = new PayVo();
        Order order = get(orderSn);
        BigDecimal payAmount = order.getPayAmount().setScale(2, RoundingMode.UP);
        payVo.setTotalAmount(payAmount.toString());
        payVo.setOutTradeNo(order.getOrderSn());
        List<OrderItem> orderItems = orderItemService.list(new QueryWrapper<OrderItem>().eq("order_sn", orderSn));
        payVo.setSubject(orderItems.get(0).getSkuName());
        payVo.setBody(orderItems.get(0).getSkuAttrsVal());
        return payVo;
    }

    @Override
    public PageVo<Order> pageWithItem(WebParamVo param) {
        LambdaQueryWrapper<Order> wrapper = Wrappers.<Order>lambdaQuery()
                .eq(param.getMemberId() != null, Order::getMemberId, param.getMemberId())
                .orderByDesc(Order::getId);
        Page<Order> orderPage = baseMapper.selectPage(Page.of(param.getPageNum(), param.getPageSize()), wrapper);
        List<Order> result = orderPage.getRecords().stream()
                .peek(order -> order.setOrderItems(orderItemService.get(order.getOrderSn())))
                .collect(Collectors.toList());
        orderPage.setRecords(result);
        return PageUtil.toPage(orderPage);
    }

    @Override
    public boolean handlePayResult(PayAsyncVo vo) throws ExecutionException, InterruptedException {
        // 保存交易流水记录
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setOrderSn(vo.getOut_trade_no());
        paymentInfo.setTradeNo(vo.getTrade_no());
        paymentInfo.setPaymentStatus(vo.getTrade_status());
        paymentInfo.setCallbackTime(vo.getNotify_time());
        paymentInfo.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
        paymentInfo.setSubject(vo.getSubject());
        paymentInfo.setConfirmTime(vo.getNotify_time());
        paymentInfo.setCreateTime(vo.getGmt_create());
        paymentInfo.setCallbackContent(vo.getBody());
        paymentInfoService.save(paymentInfo);

        log.info("handlePayResult => {}", paymentInfo);
        // 修改订单状态
        if (vo.paySuccess()) {
            String orderSn = vo.getOut_trade_no();
            // 更新并查询出订单，获取付款人id
            CompletableFuture<Order> orderTask = CompletableFuture.supplyAsync(() -> {
                Order order = new Order().setStatus(OrderConstant.StatusEnum.PAYED.getCode())
                        .setPaymentTime(new Date()).setModifyTime(new Date());
                this.baseMapper.update(order, new QueryWrapper<Order>().eq("order_sn", orderSn));
                return this.get(orderSn);
            }, executor);

            CompletableFuture<Map<Integer, List<OrderItem>>> orderItemTask = CompletableFuture.supplyAsync(() -> {
                // 查询出具体订单项，进而订单项获取购买的skuId
                List<OrderItem> orderItems = orderItemService.list(new QueryWrapper<OrderItem>()
                        .eq("order_sn", orderSn));
                return orderItems.stream()
                        .collect(Collectors.groupingBy(OrderItem::getPromotion, Collectors.toList()));
            }, executor);

            CompletableFuture.allOf(orderTask, orderItemTask).get();
            String cartKey;
            for (Integer promotion : orderItemTask.get().keySet()) {
                if (promotion == ProductConstant.PromotionEnum.PROMOTION.getCode()) {
                    cartKey = CouponConstant.CART_SECKILL_CACHE_PREFIX + orderTask.get().getMemberId();
                } else {
                    cartKey = CartConstant.CART_PREFIX + orderTask.get().getMemberId();
                }
                // 通过skuId删除支付成功的购物项
                for (Long skuId : orderItemTask.get().get(promotion)
                        .stream().map(OrderItem::getSkuId).collect(Collectors.toList())) {
                    stringRedisTemplate.boundHashOps(cartKey).delete(skuId.toString());
                }
            }

        }
        return true;
    }

    @Override
    public void createSeckillCartItem(SeckillOrderTo seckillOrder) throws ExecutionException, InterruptedException {
        String cartKey = CouponConstant.CART_SECKILL_CACHE_PREFIX + seckillOrder.getMemberId();
        BoundHashOperations<String, String, String> cartOption = stringRedisTemplate.boundHashOps(cartKey);
        // 异步获取skuInfo信息
        CompletableFuture<SkuInfoVo> skuInfoTask = CompletableFuture
                .supplyAsync(() -> {
                    Result skuResp = productFeign.getSku(seckillOrder.getSkuId());
                    return skuResp.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                    });
                }, executor);

        // 等待任务完成才能继续往下执行
        CompletableFuture.allOf(skuInfoTask).get();

        // 将商品添加到购物车中
        CartVo.CartItem cartItem = new CartVo.CartItem();
        cartItem.setSkuId(skuInfoTask.get().getSkuId());
        cartItem.setSelected(true);
        cartItem.setTitle(skuInfoTask.get().getSkuTitle());
        cartItem.setImage(skuInfoTask.get().getSkuDefaultImg());
        // 这里是秒杀价格
        cartItem.setPrice(seckillOrder.getSeckillPrice());
        cartItem.setCount(seckillOrder.getNum());

        String cartItemJson = JSON.toJSONString(cartItem);
        cartOption.put(seckillOrder.getSkuId().toString(), cartItemJson);
    }

    @Override
    public OrderDetailTo detail(String orderSn) {
        OrderDetailTo detail = new OrderDetailTo();
        Order order = this.get(orderSn);
        detail.setPayPrice(order.getPayAmount());
        detail.setOrder(order.toTo());
        List<OrderItem> orderItems = orderItemService.get(orderSn);
        List<OrderItemTo> orderItemTos = orderItems.stream().map(OrderItem::toTo).collect(Collectors.toList());
        detail.setOrderItems(orderItemTos);
        return detail;
    }

    @Override
    public Map<String, List<?>> getQuarterlyEarning() {
        List<QuarterlyDataVo> quarterlyEarnings = baseMapper.getQuarterlyEarning();
        List<BigDecimal> temp = Stream.of(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO).collect(Collectors.toList());
        for (QuarterlyDataVo quarterlyEarning : quarterlyEarnings) {
            temp.set(quarterlyEarning.getQuarter() - 1, quarterlyEarning.getMoneyTotal());
        }

        // 获取有效订单
        BigDecimal total = this.baseMapper.getTotalIncome();
        List<BigDecimal> totalList = new ArrayList<>(Collections.singletonList(total));
        // 结果封装
        Map<String, List<?>> res = new HashMap<>(5);
        List<String> quarters = new ArrayList<>(Arrays.asList("第一季度", "第二季度", "第三季度", "第四季度"));
        // JSON数组
        JSONArray jsonArray = new JSONArray();
        for (int i = 0; i < temp.size(); i++) {
            if (temp.get(i).compareTo(BigDecimal.ZERO) > 0) {
                // JSON对象
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("name", quarters.get(i));
                jsonObject.put("value", temp.get(i));
                jsonArray.add(jsonObject);
            }
        }
        List<String> title = new ArrayList<>(Collections.singletonList("商品收益"));
        res.put("xAbscissa", quarters);
        res.put("blueData", temp);
        res.put("titles", title);
        res.put("moneyTotal", totalList);
        res.put("quarterlyEarning", jsonArray);
        return res;
    }

    @Override
    public Map<String, List<?>> getWeeklyData() {
        // 近一周订单量
        List<WeeklyDataVo> weeklyData = baseMapper.getWeeklyData();
        // 获取周
        List<String> weekend = weeklyData.stream().map(WeeklyDataVo::getWeekend).collect(Collectors.toList());
        // 获取近一周商品订单详细单数
        List<Integer> weeklyCount = weeklyData.stream().map(WeeklyDataVo::getCount).collect(Collectors.toList());
        // 获取有效订单总数
        Long count = this.baseMapper.selectCount(new QueryWrapper<Order>().in("status", 1, 2, 3));
        // 用List存放订单总数
        List<Integer> orderCount = new ArrayList<>();
        orderCount.add(count.intValue());
        // 存放最后结果
        Map<String, List<?>> map = new HashMap<>(4);
        // 设置标题
        List<String> titles = new ArrayList<>();
        titles.add("商品订单");
        map.put("xAbscissa", weekend);
        map.put("blueData", weeklyCount);
        map.put("titles", titles);
        map.put("orderTotal", orderCount);
        return map;
    }

    @Override
    public PageVo<Order> search(BaseSearchVo search) {
        LambdaQueryWrapper<Order> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotEmpty(search.getKeyword())) {
            wrapper.like(Order::getMemberUsername, search.getKeyword())
                    .or().like(Order::getDeliveryCompany, search.getKeyword())
                    .or().like(Order::getDeliverySn, search.getKeyword())
                    .or().like(Order::getReceiverName, search.getKeyword())
                    .or().like(Order::getReceiverProvince, search.getKeyword())
                    .or().like(Order::getReceiverCity, search.getKeyword());
        }
        Page<Order> page = this.baseMapper.selectPage(Page.of(search.getPageNum(), search.getPageSize()), wrapper);
        return PageUtil.toPage(page);
    }

    /**
     * 保存订单数据
     */
    private void saveTo(OrderDetailTo orderTo) {
        Order order = Order.fromTo(orderTo.getOrder());
        order.setCreateTime(new Date());
        order.setModifyTime(new Date());
        this.save(order);
        Order orderFromDb = this.getOne(new QueryWrapper<Order>().eq("order_sn", order.getOrderSn()));
        orderTo.getOrder().setId(orderFromDb.getId());
        List<OrderItemTo> orderItemTos = orderTo.getOrderItems();
        List<OrderItem> orderItems = orderItemTos.stream().map(orderItemTo -> {
            OrderItem orderItem = OrderItem.fromTo(orderItemTo);
            orderItem.setOrderId(orderFromDb.getId());
            return orderItem;
        }).collect(Collectors.toList());
        orderItemService.saveBatch(orderItems);
    }

    private OrderDetailTo create() {
        OrderSubmitVo orderSubmit = submitThreadLocal.get();
        OrderDetailTo orderTo = new OrderDetailTo();
        Order order = new Order();

        // 订单号
        String orderSn = IdWorker.getTimeId().substring(0, 32);
        order.setOrderSn(orderSn);
        // 状态
        order.setStatus(OrderConstant.StatusEnum.CREATE_NEW.getCode());
        // 其他信息...
        order.setMemberId(orderSubmit.getMemberId());
        order.setMemberUsername(orderSubmit.getMemberUserName());
        order.setPayType(orderSubmit.getPayType());
        order.setSourceType(OrderConstant.SourceType.PC.getType());

        fillAddress(orderSubmit.getAddressId(), order);

        // 获取订单项
        List<OrderItemTo> orderItems = new ArrayList<>();
        Result cartItemRes = productFeign.getCurrentUserCartItem(orderSubmit.getMemberId());
        if (cartItemRes.getCode() == SystemConstant.Status.SUCCESS.getCode()) {
            List<OrderConfirmVo.CartItemVo> cartItemVos = cartItemRes.getData("cartItems",
                    new TypeReference<List<OrderConfirmVo.CartItemVo>>() {
                    });
            if (!CollectionUtils.isEmpty(cartItemVos)) {
                orderItems = cartItemVos.stream().map(cartItemVo -> {
                    OrderItemTo orderItem = buildOrderItem(cartItemVo);
                    orderItem.setOrderSn(orderSn);
                    return orderItem;
                }).collect(Collectors.toList());
            }
        }

        // 验价
        priceChecking(order, orderItems);

        orderTo.setOrder(order.toTo());
        orderTo.setOrderItems(orderItems);
        orderTo.setPayPrice(order.getPayAmount());

        return orderTo;
    }

    /**
     * 为订单填充收货信息
     *
     * @param addressId 地址ID
     * @param order     订单
     */
    private void fillAddress(Long addressId, Order order) {
        Result receiveAddressRes = memberFeign.getReceiveAddress(addressId);
        if (receiveAddressRes.getCode() == SystemConstant.Status.SUCCESS.getCode()) {
            MemberReceiveAddressVo memberReceiveAddressVo = receiveAddressRes.getData("memberReceiveAddressVo",
                    new TypeReference<MemberReceiveAddressVo>() {
                    });
            if (ObjectUtils.isNotEmpty(memberReceiveAddressVo)) {
                order.setReceiverCity(memberReceiveAddressVo.getCity());
                order.setReceiverDetailAddress(memberReceiveAddressVo.getDetailAddress());
                order.setReceiverName(memberReceiveAddressVo.getName());
                order.setReceiverPhone(memberReceiveAddressVo.getPhone());
                order.setReceiverPostCode(memberReceiveAddressVo.getPostCode());
                order.setReceiverProvince(memberReceiveAddressVo.getProvince());
                order.setReceiverRegion(memberReceiveAddressVo.getRegion());
                order.setFreightAmount(BigDecimal.ZERO);
            }
        }
    }

    /**
     * 验价
     */
    private void priceChecking(Order order, List<OrderItemTo> orderItems) {
        BigDecimal total = BigDecimal.ZERO;
        // 没有任何优惠价格，订单的总额就是每个订单项的总额之和
        for (OrderItemTo orderItem : orderItems) {
            BigDecimal orderItemTotalPrice = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity().toString()));
            total = total.add(orderItemTotalPrice);
        }
        order.setTotalAmount(total);
        order.setPayAmount(total.add(order.getFreightAmount()));
    }

    /**
     * 构建订单项
     */
    private OrderItemTo buildOrderItem(OrderConfirmVo.CartItemVo cartItemVo) {
        OrderItemTo orderItem = new OrderItemTo();
        // 商品的SPU信息
        Result spuInfoRes = productFeign.getSpu(cartItemVo.getSkuId());
        SpuInfoVo spuInfoVo = spuInfoRes.getData("spuInfoVo", new TypeReference<SpuInfoVo>() {
        });
        orderItem.setSpuId(spuInfoVo.getId());
        orderItem.setSpuBrand(spuInfoVo.getBrandId().toString());
        orderItem.setSpuName(spuInfoVo.getSpuName());
        orderItem.setCategoryId(spuInfoVo.getCategoryId());

        // 商品的sku信息
        orderItem.setSkuId(cartItemVo.getSkuId());
        orderItem.setSkuName(cartItemVo.getTitle());
        orderItem.setSkuPic(cartItemVo.getImage());
        orderItem.setSkuPrice(cartItemVo.getPrice());
        String skuAttr = StringUtils.collectionToDelimitedString(cartItemVo.getSkuAttrs(), ";");
        orderItem.setSkuAttrsVal(skuAttr);
        orderItem.setSkuQuantity(cartItemVo.getCount());
        // 商品的积分信息
        orderItem.setGiftGrowth(cartItemVo.getPrice().intValue());
        orderItem.setGiftIntegration(cartItemVo.getPrice().intValue());
        orderItem.setPromotion(cartItemVo.getPromotion());

        return orderItem;
    }

    /**
     * 获取季度具体收益，方便前端展示
     *
     * @param list List<QuarterlyDataVo>
     * @return List<BigDecimal>
     */
    private List<BigDecimal> getQuarterlyMonet(List<QuarterlyDataVo> list) {
        List<Integer> quarterList = list.stream().map(QuarterlyDataVo::getQuarter).collect(Collectors.toList());
        List<BigDecimal> moneyList = list.stream().map(QuarterlyDataVo::getMoneyTotal).collect(Collectors.toList());
        List<BigDecimal> resultList;
        // 判断是否为空
        if (quarterList.size() == 0) {
            resultList = new ArrayList<>(
                    Arrays.asList(new BigDecimal(0),
                            new BigDecimal(0),
                            new BigDecimal(0),
                            new BigDecimal(0)));
        } else {
            resultList = new ArrayList<>();
            for (int i = 0; i < quarterList.size(); i++) {
                for (int j = i; j < i + 1; j++) {
                    if (j + 1 == quarterList.get(i)) {
                        resultList.add(j, moneyList.get(i));
                    } else {
                        resultList.add(j, new BigDecimal(0));
                    }
                }
            }
        }

        return resultList;
    }
}
